#include "CQuery.h"
#include "Utils.h"
#include <wx/event.h>

CQuery::CQuery(
               #ifdef GUI_DEPENDENT
                    wxEvtHandler* pGaugeEventReciever
                #endif
               )
:resultBLOB(0)
#ifdef GUI_DEPENDENT
,gaugeEventReciever(pGaugeEventReciever)
#endif
{
    wxSQLite3Database::InitializeSQLite();
    if(! db.IsOpen())
    {
        #ifdef SQL_DEBUG
            wxMessageBox("open DB");
        #endif
        #ifdef SQL_DEBUG_LOG
            LOGMAN.logInfo("open DB");
        #endif
        db.Open(dbPath);
    }

    initNewDB();
    #ifdef SQL_DEBUG
        wxMessageBox("init DB");
    #endif
    #ifdef SQL_DEBUG_LOG
        LOGMAN.logInfo("init DB");
    #endif
}

CQuery::~CQuery()
{
    if(db.IsOpen())
    {
        db.Close();
    }

    if(resultBLOB != NULL)
    {
        delete resultBLOB;
        resultBLOB = NULL;
    }
}

/***************Class  Private  misc**********************/
wxString CQuery::dbPath(wxGetCwd() +_T("/")+_T(DEFAULT_DB_NAME));
long CQuery::num=0;

inline void CQuery::setDBpath(const wxString& db_name_IN) throw ()
{
    dbPath = wxGetCwd() +_T("/")+db_name_IN;
}

inline wxString CQuery::createTableSQL(const wxString& tableName) throw ()
{
    return wxString("")<<CREATE_TABLE_FOLLOWERS_header
                                 <<tableName
                                 <<CREATE_TABLE_FOLLOWERS_footer;
}


void CQuery::initNewDB()
{
    try
    {

        if(!(db.TableExists(_T("T_system"))))
        {
            #ifdef SQL_DEBUG
                wxMessageBox("create T_system");
            #endif
            #ifdef SQL_DEBUG_LOG
                LOGMAN.logInfo("create T_system");
            #endif
            db.ExecuteUpdate(CREATE_TABLE_SYSTEM);
            setSystemParam("rate_used","0");
        }

        if(!(db.TableExists(_T("T_followers_record"))))
        {
            #ifdef SQL_DEBUG
                wxMessageBox("create T_followers_record");
            #endif
            #ifdef SQL_DEBUG_LOG
                LOGMAN.logInfo("create T_followers_record");
            #endif
            db.ExecuteUpdate(createTableSQL("T_followers_record"));
        }

        if(!(db.TableExists(_T("T_followers_recycler"))))
        {
            #ifdef SQL_DEBUG
                wxMessageBox("create T_followers_recycler");
            #endif
            #ifdef SQL_DEBUG_LOG
                LOGMAN.logInfo("create T_followers_recycler");
            #endif
            db.ExecuteUpdate(wxString(CREATE_TABLE_FOLLOWERS_header)
                             <<wxString("T_followers_recycler")
                             <<CREATE_TABLE_RECYCLER_footer);
        }
    }
    catch(wxSQLite3Exception e)
    {
        #ifdef SQL_DEBUG
            wxMessageBox(wxString("When init DB , ")<<(e.GetErrorCode()+":"+e.GetMessage()));
        #endif
        #ifdef SQL_LOG
            LOGMAN.logError(wxString("When init DB , ")<<(e.GetErrorCode()+":"+e.GetMessage()));
        #endif
    }
}

bool CQuery::extractResult(wxSQLite3ResultSet& queryResult)
{
    std::map<wxString,wxString> twoDtable;

    try
    {
        while (queryResult.NextRow())
        {
        //columns iteration
            for(int columnIdx=0;columnIdx<queryResult.GetColumnCount();++columnIdx)
            {
                twoDtable[queryResult.GetColumnName(columnIdx)] = queryResult.GetString(columnIdx);
            }
            result.push_back(twoDtable);
        }
    }
    catch(wxSQLite3Exception e)
    {
        #ifdef SQL_DEBUG
            wxMessageBox(wxString("When extract result , ")<<(e.GetErrorCode()+":"+e.GetMessage()));
        #endif
        #ifdef SQL_LOG
            LOGMAN.logError(wxString("When extract result , ")<<(e.GetErrorCode()+":"+e.GetMessage()));
        #endif
        return false;
    }

    return true;
}



/*******************     Class  API      **********************/

int CQuery::countTable(const wxString& tableName, const wxString& keyField)
{
    int num=-1;
    try
    {
        tableType res;
        res = query(wxString("SELECT count(")+keyField+") FROM "+tableName);
        if(res.size() != 0)
        {
            res[0]["count("+keyField+")"].ToLong((long*)&num);
        }
    }
    catch(wxSQLite3Exception e)
    {
        #ifdef SQL_DEBUG
            wxMessageBox(wxString("When count table ")<<tableName<<wxString(", got an error ")<<(e.GetErrorCode()+":"+e.GetMessage()));
        #endif
        #ifdef SQL_LOG
            LOGMAN.logError(wxString("When count table ")<<tableName<<wxString(", got an error ")<<(e.GetErrorCode()+":"+e.GetMessage()));
        #endif
    }

    return num;
}

tableType& CQuery::query(const wxString& sql_IN)
{
    wxSQLite3ResultSet  queryResult;
    result.clear();

    try
    {
        queryResult = db.ExecuteQuery(sql_IN);
        if(extractResult(queryResult))
        {
            queryResult.Finalize();
            return result;
        }
    }
    catch(wxSQLite3Exception e)
    {
        #ifdef SQL_DEBUG
            wxMessageBox(wxString("In generic query , ")<<(e.GetErrorCode()+":"+e.GetMessage()+"-->SQL: "+sql_IN));
        #endif
        #ifdef SQL_LOG
            LOGMAN.logError(wxString("In generic query , ")<<(e.GetErrorCode()+":"+e.GetMessage()+"-->SQL: "+sql_IN));
        #endif
        return result;
    }

    //If extractResult(queryResult) raise an exception, it will return false , and reach here
    return result;
}

wxMemoryBuffer* CQuery::queryBLOB(const wxString& sql_IN)
{

    if(resultBLOB!= NULL)
    {
        delete resultBLOB;
        resultBLOB = 0;
    }

    resultBLOB = new wxMemoryBuffer(0); // saved the copy during function return

    try
    {
        wxSQLite3ResultSet  queryResultBLOB = db.ExecuteQuery(sql_IN);
        queryResultBLOB.GetBlob(0,*resultBLOB);  //Note : this col index start with 0 , append to MB
        queryResultBLOB.Finalize();
    }
    catch(wxSQLite3Exception e)
    {
        #ifdef SQL_DEBUG
            wxMessageBox(wxString("In queryBLOB , ")<<(e.GetErrorCode()+":"+e.GetMessage()+"-->SQL: "+sql_IN));
        #endif
        #ifdef SQL_LOG
            LOGMAN.logError(wxString("In queryBLOB , ")<<(e.GetErrorCode()+":"+e.GetMessage()+"-->SQL: "+sql_IN));
        #endif

        return 0;
    }

    return resultBLOB;
}

bool CQuery::store(tableType& ramData, const wxString& tableName, long goalNum)
{

    typedef std::map<wxString,wxString> rowType;
    //wxString sqlStr("INSERT OR REPLACE INTO "+tableName);
    wxString sqlStr("");
    wxString colNamesStr("");
    wxString colValuesStr("");
    wxString currentValueStr("");

    for(tableType::iterator vec_itr = ramData.begin(); vec_itr != ramData.end(); ++vec_itr)
    {
        sqlStr = "INSERT OR REPLACE INTO "+tableName;
        colNamesStr = "";
        colValuesStr = "";

        for(rowType::iterator map_itr = vec_itr->begin();map_itr != vec_itr->end(); ++map_itr)
        {
            if(colNamesStr != "")
            {
                colNamesStr += ",";
            }
            colNamesStr += map_itr->first;

            if(colValuesStr != "")
            {
                colValuesStr += ",";
            }
            currentValueStr = map_itr->second;
            UT_SPECIAL_CHARS(currentValueStr);
            colValuesStr +="'"+currentValueStr+"'";
        }

        sqlStr += "("+colNamesStr+")"+" "+"VALUES ("+colValuesStr+")";
        #ifdef SQL_DEBUG
            wxMessageBox(sqlStr);
        #endif
        try
        {
            db.ExecuteUpdate(sqlStr);
        }
        catch(wxSQLite3Exception e)
        {
            #ifdef SQL_DEBUG
                wxMessageBox(wxString("In generic store() , ")<<(e.GetErrorCode()+":"+e.GetMessage()+"-->SQL: "+sqlStr));
            #endif
            #ifdef SQL_LOG
                LOGMAN.logError(wxString("In generic store() , ")<<(e.GetErrorCode()+":"+e.GetMessage()+"-->SQL: "+sqlStr));
            #endif
            return false;
        }


        #ifdef GUI_DEPENDENT
         if(goalNum != -1)
        {
            ++num;
            int percent = (int)(static_cast<float>(num)/static_cast<float>(goalNum)*100);
            if(percent>100) percent=100;    //In very few cases , the percent can be up to 130%
            SUB_GAUGE_UPDATE("", percent, gaugeEventReciever, __LINE__);
        }
        #endif
    }

    #ifdef MT_DEBUG_LOG
        LOGMAN.logInfo(wxString("[MT DEBUG], store() size: ")<<ramData.size());
    #endif

    return true;
}

bool CQuery::storeBLOB(const wxMemoryBuffer& blobValue,
               const wxString& tableName,
               const wxString& colName,
               const wxString& whereExpr)
{
    wxString sqlStr("UPDATE OR REPLACE ");
    sqlStr<<tableName<<wxString(" SET ")<<colName<<wxString("=? WHERE ")<<whereExpr;

    try
    {
        wxSQLite3Statement stm(db.PrepareStatement(sqlStr));
        stm.Bind(1,blobValue);  //Note : the index of the first param is 1, by doc
        stm.ExecuteUpdate();
    }
    catch(wxSQLite3Exception e)
    {
        #ifdef SQL_DEBUG
            wxMessageBox(wxString("When store a BLOB into ")
                         <<colName<<wxString(" in ")<<tableName<<wxString(", ")
                         <<(e.GetErrorCode()+":"+e.GetMessage()));
        #endif
        #ifdef SQL_LOG
            LOGMAN.logError(wxString("When store a BLOB into ")
                         <<colName<<wxString(" in ")<<tableName<<wxString(", ")
                         <<(e.GetErrorCode()+":"+e.GetMessage()));
        #endif
        return false;
    }

    return true;
}

wxString CQuery::getSystemParam(const wxString& keyStr)
{
    wxString sqlStr("SELECT value FROM T_system WHERE key=");
    sqlStr += "'"+keyStr+"'";
    query(sqlStr);
    if(result.size() != 0)  //in case that result is null , [0] will crash the program
    {
        return result[0]["value"];
    }
    else
    {
        return "";
    }
}

bool CQuery::setSystemParam(const wxString& keyStr , const wxString& valueStr)
{
    tableType data;
    std::map<wxString , wxString> cell;
    if(keyStr != "" && valueStr != "")
    {
        cell["key"] = keyStr;
        cell["value"] = valueStr;
        data.push_back(cell);
        return store(data, "T_system");
    }

    //return true for generic processing algorithm
    return true;
}

bool CQuery::deleteSystemParam(const wxString& keyStr)
{
    try
    {
        db.ExecuteUpdate(wxString("DELETE FROM T_System WHERE key='")<<keyStr<<wxString("'"));
    }
    catch(wxSQLite3Exception e)
    {
        #ifdef SQL_DEBUG
            wxMessageBox(wxString("When delete system param [")<<keyStr<<wxString("] , ")<<(e.GetErrorCode()+":"+e.GetMessage()));
        #endif
        #ifdef SQL_LOG
            LOGMAN.logError(wxString("When delete system param [")<<keyStr<<wxString("] , ")<<(e.GetErrorCode()+":"+e.GetMessage()));
        #endif
        return false;
    }
    return true;
}
